#include "StdAfx.h"
#include "Dictionary.h"
#include <iostream>
#include <fstream>
#include <time.h>

CDictionary::CDictionary(void)
{
}


CDictionary::~CDictionary(void)
{
	cout << "\r\nclean up, it may be took some minutes...\r\n" ;
	
	time_t startTime = time(NULL);

	for(ListWords::iterator it1 = m_category.begin();
		it1 != m_category.end(); it1++)
	{
		it1->second.clear();
	}
	m_category.clear();

#ifdef _DEBUG
	int count = 0;
	int total = m_dictionary.size();
#endif	

	for(MapWords::iterator it2 = m_dictionary.begin();
		it2 != m_dictionary.end();)
	{
		delete it2->second;
		it2->second = NULL;
		m_dictionary.erase( it2++ );

#ifdef _DEBUG
		if ( ++count % 10000 == 0 )
		{
			cout <<  "free memory: " << count * 100.0 / total  << "%\r\n";
		}
#endif	
	}

	m_dictionary.clear();

#ifdef _DEBUG
	time_t endTime = time(NULL);
	cout<< "free memory: 100%. " ;
	cout << "it tooks "  << difftime(endTime, startTime) <<  " seconds." << endl;
#endif		
}


bool CDictionary::ReadWordsFromFiles(const string& fileName)
{
	bool ret = true;
	
	time_t startTime = time(NULL);
    
	ifstream inputFile;
	cout << "loading dictionary from " << fileName << endl;

	try
	{
		inputFile.open(fileName.c_str());
		if (inputFile.is_open())
		{
			string line;
			int count = 0;
			while (getline(inputFile, line))
			{
				count += ParseLine(line);
				//cout << line << endl;
			}
			
			cout << "total words: " << count << endl;
			cout << "length \tcount" << endl;
			cout << "-------------------------------------" << endl;
			for(ListWords::const_iterator itr = m_category.begin();
				itr != m_category.end(); itr++)
			{
				cout << itr->first << "\t"  << itr->second.size() << endl;
			}
			
		}
		else
		{
			cerr << "loaded failed"  << endl;
		}
	}
	catch(exception &e)
	{
		cerr << e.what() << endl;
		ret =  false;
		throw e;
	}

	time_t endTime = time(NULL);
	cout << "\r\nload done. It tooks "  << difftime(endTime, startTime) <<  " seconds.\r\n" << endl;

	cout << "memory usage:" << endl;
	cout << "hash map 1(string -> List<string>): " << GetMemoryUsage(1) << endl;
	cout << "hash map 2(int -> List<Pointer>: " << GetMemoryUsage(2) << endl;
	
	return ret; 
}

size_t CDictionary::GetMemoryUsage(int i)
{
	size_t total = 0;
	if (i == 1)
	{
		for(MapWords::const_iterator itr = m_dictionary.begin();
			itr != m_dictionary.end(); itr++)
		{
			total += sizeof(itr->first);
			total += itr->second->GetMemoryUsage();
		}
	}
	else
	{
		for(ListWords::const_iterator itr = m_category.begin();
			itr != m_category.end(); itr++)
		{
			total += sizeof(itr->first);
			total += sizeof(CWord*) * itr->second.size();
		}
	}

	return total;
}

int CDictionary::GetLength(const map<char,int> & m)
{
	int count = 0;
	for(map<char,int>::const_iterator itr = m.begin();
		itr != m.end(); itr++)
	{
		count += itr->second;
	}
	return count;
}

int CDictionary::ParseLine(const string& line)
{
	map<char, int> keys = CWord::MakeKeys(line);
	string keyId = CWord::GetKeyId(keys);

	MapWords::iterator itr = m_dictionary.find(keyId);

	if (itr != m_dictionary.end())
	{
		itr->second->AppendWord(line);
	}
	else
	{
		CWord* word = new CWord(line, keys, keyId);
		m_dictionary.insert(make_pair(keyId, word));

		int len = GetLength(keys);
		ListWords::iterator itr = m_category.find(len);
		if (itr == m_category.end())
		{
			list<CWord*> l;
			l.push_back(word);
			m_category.insert(make_pair(len, l));
		}
		else
		{
			itr->second.push_back(word);
		}
	}
	return 1;
}

int CDictionary::GetCandidates(
	const int length,  
	const map<char,int>& goal,
	list<CWord*>& candidates)
{
	int count = 0;

	ListWords::iterator itr = m_category.find(length);

	if (itr != m_category.end())
	{
		candidates.clear();
		for(list<CWord*>::const_iterator it = itr->second.begin();
			it != itr->second.end(); it++)
		{
			if ((*it)->IsSubSet(goal))
			{
				candidates.push_back((*it));
				++count;
			}
		}
	}

	return count;
}

int CDictionary::Search(const list<CWord*>& wordList1, list<CWord*> wordList2, const map<char, int>& goal)
{
	int count = 0;

	list<CWord*>::const_iterator itr1;
	for(itr1 = wordList1.begin(); itr1 != wordList1.end(); itr1++)
	{
		list<CWord*>::const_iterator itr2;
		for(itr2 = wordList2.begin(); itr2 != wordList2.end(); itr2++)
		{
			if ( (*itr1)->IsEqualIfAdd((*itr2)->GetKeys(), goal))
			{
				Print((*itr1)->GetWordList(), (*itr2)->GetWordList());
				++count;
			}
		}
	}

	return count;
}

void CDictionary::Print(const list<string>& wordList1, const list<string>& wordList2)
{
	for(list<string>::const_iterator itr1 = wordList1.begin();
		itr1 != wordList1.end(); itr1++)
	{
		for(list<string>::const_iterator itr2 = wordList2.begin();
			itr2 != wordList2.end(); itr2++)
		{
			cout << (*itr1) << "," << (*itr2) << endl;
		}
	}
}